package znet

import (
	"errors"
	"gitee.com/hufengjiu/go-zinx/utils"
	"gitee.com/hufengjiu/go-zinx/ziface"
	"log"
	"net"
	"sync"
)

type Connection struct {
	Server     ziface.IServer
	Conn       *net.TCPConn
	ConnId     uint32
	isClosed   bool
	mux        ziface.IMux
	ExitChan   chan bool
	dp         *DataPack
	msgChan    chan []byte
	msgBufChan chan []byte
	properties map[string]interface{}
	propMu     sync.RWMutex
}

func NewConnection(server ziface.IServer, conn *net.TCPConn, id uint32, mux ziface.IMux) *Connection {
	c := &Connection{
		Server:     server,
		Conn:       conn,
		ConnId:     id,
		isClosed:   false,
		mux:        mux,
		ExitChan:   make(chan bool),
		dp:         NewDataPack(),
		msgChan:    make(chan []byte),
		msgBufChan: make(chan []byte, utils.GlobalConf.MaxMsgChanLen),
		properties: make(map[string]interface{}),
		propMu:     sync.RWMutex{},
	}

	c.Server.GetConnManager().Add(c)

	return c
}

func (c *Connection) Start() {
	go c.StartReader()
	go c.StartWriter()

	c.Server.CallOnConnStart(c)
}

func (c *Connection) StartReader() {
	log.Printf("accept client %s\n", c.Conn.RemoteAddr().String())
	defer c.Stop()
	for {
		//buf := make([]byte, 512)
		//cnt, err := c.Conn.Read(buf)
		//if err != nil {
		//	log.Println("read err", err)
		//	c.Stop()
		//	return
		//}

		message, err := c.dp.UnPackFromReader(c.Conn)
		if err != nil {
			log.Println("unpack error", err)
			return
		}

		log.Printf("read data len %d, data:  %s\n", message.GetDataLen(), message.GetData())
		req := &Request{
			c:       c,
			message: message,
		}
		c.mux.PushToWorker(req)
	}

}

func (c *Connection) StartWriter() {
	defer c.Stop()
	for {
		select {
		case msg := <-c.msgChan:
			_, err := c.Conn.Write(msg)
			if err != nil {
				log.Printf("write error %v", err)
				return
			}
		case msg := <-c.msgBufChan:
			_, err := c.Conn.Write(msg)
			if err != nil {
				log.Printf("write error %v", err)
				return
			}
		case <-c.ExitChan:
			return
		}
	}
}

func (c *Connection) Stop() {
	log.Printf("[Stop] conn stop")
	if c.isClosed == true {
		return
	}
	c.Server.CallOnConnStop(c)

	c.isClosed = true
	c.Conn.Close()

	c.ExitChan <- true

	c.Server.GetConnManager().Remove(c)
	close(c.ExitChan)
	close(c.msgChan)
	close(c.msgBufChan)
}

func (c *Connection) GetTcpConn() *net.TCPConn {
	return c.Conn
}

func (c *Connection) GetConnId() uint32 {
	return c.ConnId
}

func (c *Connection) RemoteAddr() *net.Addr {
	addr := c.Conn.RemoteAddr()
	return &addr
}

func (c *Connection) Send(msgId uint32, data []byte) error {
	message := NewMessage(msgId, data)
	// 封包
	packet, err := c.dp.Pack(message)
	if err != nil {
		return err
	}
	c.msgChan <- packet
	return nil
}
func (c *Connection) SendHasBuf(msgId uint32, data []byte) error {
	message := NewMessage(msgId, data)
	// 封包
	packet, err := c.dp.Pack(message)
	if err != nil {
		return err
	}
	c.msgBufChan <- packet
	return nil
}

func (c *Connection) SetProperty(key string, value interface{}) {
	c.propMu.Lock()
	defer c.propMu.Unlock()
	c.properties[key] = value
}

func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propMu.RLock()
	defer c.propMu.RUnlock()

	if value, ok := c.properties[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("no found property")
	}
}

func (c *Connection) RemoveProperty(key string) {
	c.propMu.Lock()
	defer c.propMu.Unlock()
	delete(c.properties, key)
}
